home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / security / cops / cops_104 / src / pass.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-03-10  |  17.0 KB  |  739 lines

  1. #include <stdio.h>
  2. #include <pwd.h>
  3. #include <ctype.h>
  4.  
  5. /* C2 stuff by Ole H. Nielsen */
  6. #ifdef C2
  7. #include <sys/label.h>
  8. #include <sys/audit.h>
  9. #include <pwdadj.h>
  10. #endif C2
  11.  
  12. /* number of words the dictionary can suck up */
  13. #define ARB_CONST    32000
  14.  
  15. #ifndef lint
  16. static char *rcsid = "$Header: pwchkr.c,v 1.1 85/09/10 16:00:56 root Exp $";
  17. #endif
  18.  
  19. /*
  20.  * Warning: this program burns a lot of cpu.
  21.  */
  22. /*
  23.  * Insecure - find accounts with poor passwords
  24.     Date: Tue, 29 Nov 83 18:19:32 pst
  25.     From: leres%ucbarpa@Berkeley (Craig Leres)
  26.  
  27.     Insecure is something that Jef Poskanzer and I wrote to rid a
  28.     local system of an overly persistent ankle-biting adolescent.
  29.     It was a quick hack we whipped up in just a few minutes and was
  30.     never intended to be publically distributed. Unfortunately, I
  31.     made the mistake of giving a copy to an associate at UC
  32.     Berkeley. Apparently, he incorporated it in a security package
  33.     he later developed for use at Berkeley. Someone else
  34.     distributed it outside Berkeley which explains why it's been
  35.     publically distributed.
  36.  
  37.  
  38.         Modified by Seth Alford, Roger Southwick, Steve Dum, and
  39.         Rick Lindsley for Tektronix
  40.  
  41.       Bits and pieces hacked by me and others, 1/4/91... df
  42.  */
  43.  
  44. /*
  45.  *    $Log:    pwchkr.c,v $
  46.  *    Revision 1.1  85/09/10  16:00:56  root
  47.  *    Initial revision
  48.  *    
  49.  *
  50.  * By default, this program only checks for accounts with passwords the same
  51.  * as the login name. The following options add more extensive checking. (The
  52.  * tradeoff is cpu time -- with all options enabled it can run into the 100's
  53.  * of MINUTES.) Any argument that does not begin with a "-" is assumed to be
  54.  * a file name. (A single '-' means stdin.) If no file name is given,
  55.  * /etc/passwd is used.
  56.  *
  57.  * Options:
  58.  *
  59.  *        -v:    verbose -- list all guesses on stdout
  60.  *        -u:    output the username on the line of the password file
  61.  *            currently being checked. If the program stops
  62.  *            abruptly you will then know how far it got.
  63.  *        -w file: use the list of words contained in "file" as likely
  64.  *            passwords. Words in the file are one to a line.
  65.  *        -b:     check all guesses backwards too
  66.  *        -g:    use the Full Name portion of the gecos field to
  67.  *            generate more guesses; also check .plan, .signature
  68.  *            and .project files.
  69.  *        -s:    check the single letters a-z, A-Z, 0-9 as passwords
  70.  *        -c:    with each guess, check for all-lowercase and
  71.  *            all-uppercase versions too.
  72.  *        -d:     check the doubling of the username
  73.  *        -n:    complain about null passwords (default is to keep quiet)
  74.  *        -p:    print the password when guessed
  75.  *        -P:    use alternate password file
  76.  */
  77.  
  78. int verbose = 0, singles = 0, backwards = 0, checkgecos = 0, checkcase = 0,
  79.     chknulls = 0, printit = 0, users = 0, chkwords = 0, checkdouble = 0;
  80.  
  81. char *my_index(), *reverse();
  82. long atol();
  83. FILE *fopen();
  84. char *fgets();
  85.  
  86. /* char PASSWD[] = "/etc/passwd"; */
  87. char PASSWD[256];
  88.  
  89. char EMPTY[] = "";
  90. static FILE *pwf = NULL, *wlf = NULL;
  91. char line[BUFSIZ+1];
  92. struct passwd passwd;
  93. char    *Curpw, *Wordlist = NULL;
  94.  
  95. main(argc, argv)
  96. char **argv;
  97. {
  98.     register int i;
  99.     register char *arg;
  100.     int onedone = 0;
  101.  
  102.     /*
  103.     You have to decide whether or not to include these lines....
  104.  
  105.     if (getuid()) {
  106.     printf("Did you really think we would let you run this?\n");
  107.     exit(1);
  108.     }
  109.  
  110.     */
  111.     strcpy(PASSWD, "/etc/passwd");
  112.  
  113.     for (i = 1; i < argc; i++)
  114.     if ((arg = argv[i]) && *arg == '-')
  115.         while (*++arg) {
  116.         switch (*arg) {
  117.             case 'n':
  118.             /*
  119.              * complain about null passwords
  120.              */
  121.             chknulls++;
  122.             break;
  123.             case 'c':
  124.             /*
  125.              * check cases
  126.              */
  127.             checkcase++;
  128.             break;
  129.             case 'g':
  130.             /*
  131.              * use gecos
  132.              */
  133.             checkgecos++;
  134.             break;
  135.             case 'v':
  136.             /*
  137.              * turn on motormouth
  138.              */
  139.             verbose++;
  140.             break;
  141.             case 'b':
  142.             /*
  143.              * check all attempts forwards and backwards
  144.              */
  145.             backwards++;
  146.             break;
  147.             case 'd':
  148.             /*
  149.             * check the doubling of the username
  150.             */
  151.             checkdouble++;
  152.             break;
  153.             case 's':
  154.             /*
  155.              * carry out a more intensive search, checking for
  156.              * single letter passwords
  157.              */
  158.             singles++;
  159.             break;
  160.             case 'p':
  161.             /*
  162.              * print out the password when found
  163.              */
  164.             printit++;
  165.             break;
  166.             case 'u':
  167.             /*
  168.              * print out users as testing
  169.              */
  170.             users++;
  171.             break;
  172.             case 'P':
  173.             /*
  174.              * use alternate passwd file
  175.              */
  176.             if (argv[i+1] == NULL) {
  177.                 fprintf(stderr,
  178.                 "%s: No file supplied with -P option\n",
  179.                 argv[0]);
  180.                 exit (1);
  181.                 }
  182.             strcpy(PASSWD, argv[i+1]);
  183.             argv[i+1] = NULL;
  184.             break;
  185.             case 'w':
  186.             /*
  187.              * consult word list of likely passwords
  188.              */
  189.             if ((Wordlist = argv[i+1]) == NULL) {
  190.                 fprintf(stderr,
  191.                 "%s: No file supplied with -w option\n",
  192.                 argv[0]);
  193.                 exit (1);
  194.                 }
  195.             argv[i+1] = NULL;
  196.             break;
  197.             case '\0':
  198.             /*
  199.              * read from stdin
  200.              */
  201.             break;
  202.             default:
  203.             fprintf(stderr,
  204.                 "%s: unknown option '%c'. Options are:\n",argv[0],
  205.                 *arg);
  206.             /* FALL THRU */
  207.             case '-':
  208.             fprintf(stderr,"-v:\t\tverbose -- list all guesses on stdout\n");
  209.             fprintf(stderr,"-u:\t\toutput the username currently being checked\n");
  210.             fprintf(stderr,"-w file:\tconsult the indicated file for words to check as passwords\n");
  211.             fprintf(stderr,"-b:\t\tcheck all guesses forwards and backwards\n");
  212.             fprintf(stderr,"-g:\t\tuse the Full name portion of the gecos field for more guesses\n");
  213.             fprintf(stderr,"-s:\t\tcheck the single letters a-z, A-Z, 0-9 as passwords\n");
  214.             fprintf(stderr,"-c:\t\tcheck the all-upper and all-lower case version of each guess\n");
  215.             fprintf(stderr,"-d:\t\tcheck for double repetition of the username\n");
  216.             fprintf(stderr,"-n:\t\tcomplain about null passwords\n");
  217.             fprintf(stderr,"-p:\t\tprint the password when guessed\n");
  218.             exit(1);
  219.             }
  220.         argv[i] = NULL;
  221.         }
  222.     
  223. #ifdef FCRYPT
  224. init_des();
  225. #endif
  226.  
  227.     for (i = 1; i < argc; i++) {
  228.     if (argv[i] == NULL) continue;
  229.     onedone++;
  230.     if (*(argv[i]) == '-') {
  231.         /*
  232.          * read from stdin; we'll cheat and set pwf directly
  233.          */
  234.         pwf = stdin;
  235.         chkpw();
  236.         /*
  237.          * don't fclose stdin!
  238.          */
  239.         clearerr(stdin);
  240.         }
  241.     else {
  242.         if ((pwf=fopen(argv[i],"r")) == NULL) {
  243.         perror(argv[i]);
  244.         continue;
  245.         }
  246.         Curpw = argv[i];
  247.         chkpw();
  248.         end2pwent();
  249.         }
  250.     }
  251.     if (!onedone) {
  252.     Curpw = NULL;
  253.     chkpw();
  254.     }
  255.     exit(0);
  256. }
  257.  
  258. /*
  259.  * Added by Jacob Gore, March 12, 1987.
  260.  *
  261.  * Finds the pointer of the leftmost occurance within the character string
  262.  * 'string' of any character found within the character string 'chars'.
  263.  *
  264.  * If none of the characters in 'chars' appear in 'string', NULL is retutned.
  265.  *
  266.  */
  267. char *
  268. indexm (string, chars)
  269.     char *string, *chars;
  270. {
  271.     while (*string) {
  272.     if (my_index(chars, *string) != NULL) {
  273.         return string;
  274.     }
  275.     string++;
  276.     }
  277.     return NULL;
  278. }
  279.  
  280. chkpw()
  281. {
  282. #ifdef C2
  283.     struct passwd_adjunct *pwdadj;
  284.     struct passwd_adjunct *getpwanam();
  285. #endif C2
  286.     register char    *cp, *cp2;
  287.     struct passwd    *pwd;
  288.     struct passwd    *getpwent();
  289.     char        guess[100];
  290.     char        *wordarray[ARB_CONST];
  291.     char        *malloc(), **wordptr, **endptr;
  292.     int            done = 0;
  293.  
  294.  
  295.     if (Wordlist) {
  296.     if ((wlf = fopen(Wordlist,"r")) == NULL) {
  297.         perror(Wordlist);
  298.         exit(1);
  299.     }
  300.  
  301.     wordptr = wordarray;
  302.     /*
  303.      * note that endptr points to space OUTSIDE of wordarray
  304.      */
  305.     endptr = wordarray + (sizeof(wordarray)/sizeof(char *));
  306.  
  307. /* printf("testing words...\n"); */
  308.     while (fscanf(wlf,"%[^\n]\n",guess) != EOF) {
  309. int i;
  310. /* printf("%d => %s\n", ++i, guess); */
  311.  
  312.         if (wordptr == endptr) {
  313.         fprintf(stderr,"Ran out of wordlist space. ARB_CONST %d must be too small.\n", ARB_CONST);
  314.         exit(1);
  315.         }
  316.         if ((*wordptr = malloc(1+strlen(guess))) == NULL) {
  317.         fprintf(stderr,"malloc: no more memory for wordlist\n");
  318.         exit (1);
  319.         }
  320.         strcpy(*wordptr,guess);
  321.         wordptr++;
  322.   /* SunOs 4.03 on a Sun 3/80 didn't work properly, needed this one line fix */
  323.         if (feof(wlf)) break;
  324.     }
  325.     *wordptr = NULL;
  326.     fclose(wlf);
  327.     }
  328.  
  329.     while ((pwd = getpwent()) != 0 ) {
  330.  
  331.         done = 0;
  332.     if (verbose || users) {
  333.         if (Curpw == NULL)
  334.         printf("\t%s \"%s\"\n", pwd->pw_name, pwd->pw_gecos);
  335.         else
  336.         printf("%s -- \t%s \"%s\"\n", Curpw, pwd->pw_name,
  337.             pwd->pw_gecos);
  338.         fflush(stdout);
  339.         }
  340. #ifdef C2
  341.     (void) sprintf (guess, "##%s", pwd->pw_name);
  342.     if (strcmp (guess, pwd->pw_passwd)) {
  343.         /* Standard /etc/passwd entry */
  344.         if (verbose || users) {
  345.             if (*pwd->pw_passwd != '*' && *pwd->pw_passwd != '\0')
  346.                 printf ("\tC2 Warning!  user password %s is in the regular passwd file\n",
  347.                     pwd->pw_passwd);
  348.             fflush(stdout);
  349.         }
  350.     } else {
  351.         /* Entry in /etc/security/passwd.adjunct (C2 security) */
  352.         /* Extract the C2 security password */
  353.         if (Curpw == NULL)
  354.             pwdadj = getpwanam(pwd->pw_name);
  355.         else
  356.             pwdadj = getpwanam(Curpw);
  357.         if (pwdadj == (struct passwd_adjunct *)NULL) {
  358.             fprintf (stderr, "Failed to get the C2 secure passwd for %s\n", pwd->pw_name);
  359.             fflush(stderr);
  360.             continue;
  361.         } else
  362.             /* Substitute the C2 secure password */
  363.             pwd->pw_passwd = pwdadj->pwa_passwd;
  364.     }
  365. #endif C2
  366.  
  367.     if (*pwd->pw_passwd == '\0') {
  368.         if (chknulls) {
  369.         if (Curpw == NULL)
  370.             printf("Warning!  Password Problem: null passwd:\t%s\tshell: %s\n",
  371.             pwd->pw_name, pwd->pw_shell);
  372.         else
  373.             printf("Warning!  %s -- Password Problem: null passwd:\t%s\tshell: %s\n",
  374.             Curpw, pwd->pw_name, pwd->pw_shell);
  375.         fflush(stdout);
  376.         }
  377.         continue;
  378.     }
  379.  
  380.     /* if (strlen(pwd->pw_passwd) != 13) { continue; } */
  381.     /* common way of disabling account is a "*" in the first char of p/w */
  382.     if (*pwd->pw_passwd == '*' || strlen(pwd->pw_passwd) < 13) continue;
  383.     if (strlen(pwd->pw_passwd) > 13)
  384.          strncpy(pwd->pw_passwd, pwd->pw_passwd, 13);
  385.  
  386.     /*
  387.      * Try the user's login name
  388.      */
  389.     if (uandltry(pwd,pwd->pw_name))
  390.         continue;
  391.  
  392.     if (checkdouble) {
  393.         strcpy(guess,pwd->pw_name);
  394.         strcat(guess,pwd->pw_name);
  395.         if (uandltry(pwd,guess))
  396.             continue;
  397.         }
  398.  
  399.     /*
  400.      * Try names from the gecos field
  401.      */
  402.     if (checkgecos) {
  403.         /* Check extra files as well */
  404.         if (srch_aux_files(pwd->pw_dir, pwd)) {
  405.         done++;
  406.         continue;
  407.         }
  408.         strcpy(guess, pwd->pw_gecos);
  409.         cp = guess;
  410.         if (*cp == '-') cp++;        /* special gecos field */
  411.         if ((cp2 = my_index(cp, ';')) != NULL)
  412.         *cp2 = '\0';
  413.  
  414.         for (;;) {
  415.         /* use both ' ' and ',' as delimiters -- Jacob */
  416.         if ((cp2 = indexm(cp, " ,")) == NULL) {
  417.             if (uandltry(pwd,cp))
  418.             done++;
  419.             break;
  420.             }
  421.  
  422.         *cp2 = '\0';
  423.  
  424.         if (uandltry(pwd,cp)) {
  425.             done++;
  426.             break;
  427.             }
  428.         cp = ++cp2;
  429.         }
  430.         }
  431.         
  432.     if (!done && Wordlist)
  433.     {
  434.         /*
  435.          * try the words in the wordlist
  436.          */
  437.         wordptr = wordarray;
  438.         while (endptr != wordptr)
  439.         {
  440.         if (*wordptr == NULL)
  441.             break;
  442.         if (uandltry(pwd,*wordptr++))
  443.         {
  444.             done++;
  445.             break;
  446.         }
  447.         }
  448.     }
  449.     if (!done && singles) {
  450.         /*
  451.          * Try all single letters
  452.          * (try digits too .  --Seth)
  453.          */
  454.         guess[1] = '\0';
  455.         for (guess[0]='a'; guess[0] <= 'z'; guess[0]++)
  456.         if (try(pwd,guess))
  457.             break;
  458.         for (guess[0]='A'; guess[0] <= 'Z'; guess[0]++)
  459.         if (try(pwd,guess))
  460.             break;
  461.         for (guess[0]='0'; guess[0] <= '9'; guess[0]++)
  462.         if (try(pwd,guess))
  463.             break;
  464.         }
  465.     }
  466. }
  467.  
  468. /*
  469.  * Stands for "upper and lower" try.  Calls the "real" try, below,
  470.  * with the supplied version of the password, and with
  471.  * an upper and lowercase version of the password. If the user doesn't
  472.  * want to try upper and lower case then we just return after the one
  473.  * check.
  474. */
  475.  
  476. uandltry (pwd,guess)
  477. char *guess;
  478. struct passwd *pwd;
  479. {
  480.     register char *cp;
  481.     char buf[100];
  482.     int alllower, allupper;
  483.  
  484.     alllower = allupper = 1;
  485.  
  486.     if (try(pwd,guess) || (backwards && try(pwd,reverse(guess)))) return (1);
  487.  
  488.     if (!checkcase) return(0);
  489.  
  490.     strcpy (buf, guess);
  491.     cp = buf-1;
  492.     while (*++cp) {
  493.     if (isupper(*cp))
  494.         alllower = 0;
  495.     if (islower(*cp))
  496.         allupper = 0;
  497.     }
  498.  
  499.     if (!allupper) {
  500.     for ( cp=buf; *cp != '\0'; cp++)
  501.         if (islower (*cp))
  502.         *cp += 'A' - 'a';
  503.  
  504.     if (try(pwd,buf) || (backwards && try(pwd,reverse(buf)))) return (1);
  505.     }
  506.  
  507.     if (!alllower) {
  508.     for ( cp = buf; *cp != '\0'; cp++)
  509.         if (isupper (*cp))
  510.         *cp += 'a' - 'A';
  511.  
  512.     if (try(pwd,buf) || (backwards && try(pwd,reverse(buf)))) return (1);
  513.     }
  514.     return (0);
  515. }
  516.  
  517. try(pwd,guess)
  518. char *guess;
  519. register struct passwd *pwd;
  520. {
  521.     register char  *cp;
  522.     char   *crypt ();
  523.  
  524.     if (verbose) {
  525.     if (Curpw == NULL)
  526.         printf ("Trying \"%s\" on %s\n", guess, pwd -> pw_name);
  527.     else
  528.         printf ("%s -- Trying \"%s\" on %s\n", Curpw, guess,
  529.         pwd -> pw_name);
  530.     fflush (stdout);
  531.     }
  532.     if (! guess || ! *guess) return(0);
  533.     cp = crypt (guess, pwd -> pw_passwd);
  534.  
  535. /* silly sun tries to fool us by adding extra chars in their passwd field! */
  536. /* but laddie, we're too smart for 'em, eh?!?  Kudos to Bernard Wilson */
  537.     if (strncmp (cp, pwd -> pw_passwd, 13))
  538.     return (0);
  539.     if (Curpw == NULL)
  540.     if (printit)
  541.         printf ("Warning!  Password Problem: Guessed:\t%s\tshell: %s passwd: %s\n",
  542.         pwd -> pw_name, pwd -> pw_shell, guess);
  543.     else
  544.         printf ("Warning!  Password Problem: Guessed:\t%s\tshell: %s\n", pwd -> pw_name,
  545.         pwd -> pw_shell);
  546.     else
  547.     if (printit)
  548.         printf ("Warning!  %s -- Password Problem: Guessed:\t%s\tshell: %s passwd: %s\n",
  549.         Curpw, pwd -> pw_name, pwd -> pw_shell, guess);
  550.     else
  551.         printf ("Warning!  %s -- Password Problem: Guessed:\t%s\tshell: %s\n",
  552.         Curpw, pwd -> pw_name, pwd -> pw_shell);
  553.     fflush (stdout);
  554.     return (1);
  555. }
  556. /* end of PW guessing program */
  557.  
  558. #define MAXUID 0x7fff    /* added by tonyb 12/29/83 */
  559.             /* altered to a reasonable number - mae 8/20/84 */
  560.  
  561. end2pwent()
  562. {
  563.     fclose(pwf);
  564.     pwf = NULL;
  565. }
  566.  
  567. char *
  568. pwskip(p)
  569. register char *p;
  570. {
  571.     while(*p && *p != ':' && *p != '\n')
  572.         ++p;
  573.     if(*p == '\n')
  574.         *p = '\0';
  575.     else if(*p)
  576.         *p++ = '\0';
  577.     return(p);
  578. }
  579.  
  580. struct passwd *
  581. getpwent()
  582. {
  583.     register char *p;
  584.     long    x;
  585.  
  586.     if(pwf == NULL)
  587.         if ((pwf = fopen(PASSWD,"r")) == NULL) {
  588.         perror(PASSWD);
  589.         return(NULL);
  590.         }
  591.     p = fgets(line, BUFSIZ, pwf);
  592.     if(p == NULL)
  593.         return(0);
  594.     passwd.pw_name = p;
  595.     p = pwskip(p);
  596.     passwd.pw_passwd = p;
  597.     p = pwskip(p);
  598.     x = atol(p);    
  599.     passwd.pw_uid = (x < 0 || x > MAXUID)? (MAXUID+1): x;
  600.     p = pwskip(p);
  601.     x = atol(p);
  602.     passwd.pw_gid = (x < 0 || x > MAXUID)? (MAXUID+1): x;
  603. /*    passwd.pw_comment = EMPTY; */
  604.     p = pwskip(p);
  605.     passwd.pw_gecos = p;
  606.     p = pwskip(p);
  607.     passwd.pw_dir = p;
  608.     p = pwskip(p);
  609.     passwd.pw_shell = p;
  610.     (void) pwskip(p);
  611.  
  612.     p = passwd.pw_passwd;
  613.  
  614.     return(&passwd);
  615.  
  616. }
  617.  
  618.  
  619. /*
  620.  * reverse a string
  621.  */
  622. char *reverse(str)
  623. char *str;
  624.  
  625. {
  626.     register char *ptr;
  627.     char    *malloc();
  628.     static char buf[100];
  629.  
  630.     ptr = buf + strlen(str);
  631.     *ptr = '\0';
  632.     while (*str && (*--ptr = *str++))
  633.     ;
  634.     return(ptr);
  635.  
  636. }
  637.  
  638.  
  639. /* Guess passwords using additional files for guesses. Returns 1 (true) if
  640.  * a match was found, otherwise 0 (false). The parameters to be passed to
  641.  * this function are a character pointer to the directory in which the files
  642.  * reside. This function access the "uandltry" routine from other 
  643.  * sections of the code.
  644.  */
  645. #define MAXWORD 15        /* Maximum word length allow for guess */
  646.  
  647. #include <stdio.h>
  648. #include <ctype.h>
  649.  
  650. static char *file[] = { "/.project",        /* These are the extra files */
  651.             "/.plan",        /* to be searched for */
  652.             "/.signature",        /* prospective passwords */
  653.             "" };            /* Note the initial "/" */
  654.  
  655. int
  656. srch_aux_files(dir, pwd)
  657.     char *dir;    /* Directory in which to search */
  658.     struct passwd *pwd;    /* Encrypted password */
  659. {
  660.     char path[100];        /* Complete path */
  661.     FILE *fp;
  662.     char *wp;
  663.     char *getword();
  664.     char **p;
  665.  
  666.     p = file;
  667.     while (**p != NULL) {
  668.         strcpy(path, dir);    /* Make complete path name */
  669.         strcat(path, *p++);
  670.         if ((fp = fopen(path, "r")) == NULL)
  671.             continue;    /* If we can't open the file, skip it */
  672.         while ((wp = getword(fp)) != NULL)
  673.             if (uandltry(pwd, wp))
  674.                 return(1);
  675.         fclose(fp);
  676.     }
  677.     return(0);
  678. }
  679.  
  680. /* Get a word from a stream. Word separators are user definable in "is_sep".
  681.  * Maximum word size is MAXWORD characters. If a word reaches it's maximum
  682.  * limit, we choose not to flush the rest of the word. Returns NULL on EOF.
  683.  */
  684. char *
  685. getword(fp)
  686.     FILE *fp;
  687. {
  688.     static char word[MAXWORD + 1];
  689.     char *p = word;
  690.     int c;
  691.     int is_sep();
  692.  
  693.     while ((c = fgetc(fp)) != EOF && !isalnum(c))
  694.         ;        /* Skip over word separators */
  695.     if (c == EOF)
  696.            return(NULL);
  697.     *p++ = c;
  698.     while ((c = fgetc(fp)) != EOF && isalnum(c) && p != &(word[MAXWORD])) {
  699.         *p++ = c;    /* Quit when a word separator is encountered
  700.                  * or we reach maximum word length
  701.                  */
  702.     }
  703.     *p = '\0';        /* Mustn't forget that word terminator */
  704.     return ((c == EOF) ? NULL : word);
  705. }
  706. /* taken from comp.binaries.ibm.pc.d:
  707. Some users have reported trouble compiling the freely distributable
  708. uudecode I posted.  It seems that Berkeley moved the "index" function
  709. to one of their system libraries and some systems don't have it.
  710. Here is the missing "index" function, excerpted from an earlier freely
  711. distributable uudecode.  Just add it on the end of the uudecode I posted.
  712. */
  713. /*
  714. --Keith Petersen
  715. Maintainer of SIMTEL20's CP/M, MSDOS, & MISC archives [IP address 26.2.0.74]
  716. Internet: w8sdz@WSMR-SIMTEL20.Army.Mil, w8sdz@brl.arpa  BITNET: w8sdz@NDSUVM1
  717. Uucp: {ames,decwrl,harvard,rutgers,ucbvax,uunet}!wsmr-simtel20.army.mil!w8sdz
  718. */
  719.  
  720. /*
  721.  * Return the ptr in sp at which the character c appears;
  722.  * NULL if not found
  723.  */
  724.  
  725. #define    NULL    0
  726.  
  727. char *
  728. my_index(sp, c)
  729. register char *sp, c;
  730. {
  731.     do {
  732.         if (*sp == c)
  733.             return(sp);
  734.     } while (*sp++);
  735.     return(NULL);
  736. }
  737.  
  738.  
  739.